import { IRect } from "@fimagine/writeboard";
import { IBridging, IPoint } from "../../../Bridging";
import { rect } from "../../../get_distributions";
import { IPointerEvent } from "../../../IPointerEvent";
import { Solution } from "../../../Solution";
import { ViewPlace } from "../../../ViewPlace";
import { Info, Log } from "@fimagine/logger";
import { INFO_ARGS } from "@/pages/Chatroom/RTC/utils";


export class Bridging_HTMLElement<
  View extends HTMLElement = HTMLElement,
  ViewData = any
> implements IBridging<View> {
  protected _solution?: Solution<View, ViewData> | null;
  is_place_ok(view: View, place: ViewPlace): boolean {
    switch (place) {
      case ViewPlace.All:
      case ViewPlace.Floating:
        return true;
      case ViewPlace.Docked:
        return true;
      case ViewPlace.Minimized:
        return true;
      case ViewPlace.Maximized:
        return true;
    }
    return false
  }
  get_solution(): Solution<View> | undefined | null {
    return this._solution;
  }
  set_solution(v: Solution<View> | undefined | null): void {
    this._solution = v;
  }
  protected fn_map = new Map<View, {
    on_pointerdown?(e: PointerEvent): void;
    on_pointermove?(e: PointerEvent): void;
    on_pointerup?(e: PointerEvent): void;
  }>();
  protected view_rect_map = new Map<View, IRect>();

  @Log
  on_add(view: View): void {
    const on_pointerdown = (e: PointerEvent) => {
      const solution = this._solution;
      if (!solution) return;
      const event_adater = (e: PointerEvent): IPointerEvent<View> => ({
        button: e.button,
        target: view,
        clientX: e.clientX,
        clientY: e.clientY
      });
      solution.on_drag_begin(event_adater(e));
      if (solution.dragging !== view) return;
      const on_pointermove = (e: PointerEvent) => solution.on_drag_move(event_adater(e));
      const on_pointerup = (e: PointerEvent) => {
        solution.on_drag_end(event_adater(e));
        document.removeEventListener('pointermove', on_pointermove);
        document.removeEventListener('pointerup', on_pointerup);
      };
      document.addEventListener('pointermove', on_pointermove);
      document.addEventListener('pointerup', on_pointerup);
      const fn_pack = this.fn_map.get(view);
      if (fn_pack) fn_pack.on_pointermove = on_pointermove;
      if (fn_pack) fn_pack.on_pointerup = on_pointerup;
    };
    this.fn_map.set(view, { on_pointerdown });
    view.addEventListener('pointerdown', on_pointerdown);
  }

  @Log
  on_del(view: View): void {
    const fn_pack = this.fn_map.get(view);
    if (!fn_pack) return;
    if (fn_pack.on_pointerdown)
      view.removeEventListener('pointerdown', fn_pack.on_pointerdown);
    if (fn_pack.on_pointermove)
      document.removeEventListener('pointermove', fn_pack.on_pointermove);
    if (fn_pack.on_pointerup)
      document.removeEventListener('pointerup', fn_pack.on_pointerup);
    this.fn_map.delete(view);
    this.view_rect_map.delete(view);
  }

  private _rect_map = new Map<View, IRect>();
  get_rect(view: View): IRect {
    return this._rect_map.get(view) || this._update_rect(view);
  }
  private _update_rect(view: View) {
    let ret: IRect;
    this._rect_map.set(view, ret = rect(
      parseInt(view.style.left) || 0,
      parseInt(view.style.top) || 0,
      parseInt(view.style.width) || 0,
      parseInt(view.style.height) || 0,
    ))
    return ret
  }
  set_rect(view: View, rect: IRect): void {
    view.style.left = rect.x + 'px';
    view.style.top = rect.y + 'px';
    view.style.width = rect.w + 'px';
    view.style.height = rect.h + 'px';
    this._rect_map.set(view, { ...rect })
  }
  get_pos(view: View): IPoint {
    return {
      x: parseInt(view.style.left) || 0,
      y: parseInt(view.style.top) || 0,
    }
  }
  set_pos(view: View, { x, y }: IPoint): void {
    view.style.left = x + 'px';
    view.style.top = y + 'px';
    this._update_rect(view)
  }
  set_opacity(view: View, o: number): void {
    view.style.opacity = '' + o;
  }

  @Log
  on_drag_end(view: View): void {
    const { _solution: solution } = this;
    if (!solution) return;
    view.style.transition = '';
    if (solution.should_dock(view)) {
      solution.dock(view).update()
    } else if (solution.is_docked(view)) {
      if (solution.maximized_views.length) {
        solution.maximize(view).update()
      } else if (this.is_place_ok(view, ViewPlace.Floating)) {
        solution.restore(view).update()
      }
    }
  }
  on_drag_begin(view: View): void {
    view.style.transition = 'none';
  }
  get_z(view: View) {
    return parseInt(view.style.zIndex) || 0
  }
  set_z(view: View, z: number): void {
    view.style.zIndex = '' + z;
  }

  @Info
  on_place_changed(view: View, place: ViewPlace, prev_place: ViewPlace): void {
    view.setAttribute("place", ViewPlace[place])
    switch (place) {
      case ViewPlace.Floating:
        // view.style.resize = 'both';
        view.style.borderRadius = '20px';
        if (prev_place !== ViewPlace.Docked) {
          const rect = this.view_rect_map.get(view)
          if (rect) this._solution?.set_rect(view, rect)
        }
        break;
      case ViewPlace.Minimized:
      case ViewPlace.Maximized:
      case ViewPlace.Docked:
        this.view_rect_map.set(view, this.get_rect(view))
        // view.style.resize = 'none'
        view.style.borderRadius = ''
        break;
    }
  }

  @INFO_ARGS
  on_rect_changed(view: View, rect: IRect, prev: IRect): void {
  }

  @INFO_ARGS
  on_pos_changed(view: View, pos: IPoint, prev: IPoint): void {
  }
}
